/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.blockly.ohos;

import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.*;
import ohos.agp.utils.Rect;
import ohos.agp.utils.RectFloat;
import ohos.global.resource.Resource;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Position;
import ohos.media.image.common.Size;
import ohos.utils.Pair;

import java.util.ArrayList;
import java.util.List;

public class NinePatchElement extends PixelMapElement {

    protected PixelMap mUserPixelMap;
    private Paint mPaint;

    public NinePatchElement(PixelMap pixelMap) {
        super(createDummyPixelMap());
        mUserPixelMap = pixelMap;
        init();
    }

    public NinePatchElement(Resource resource) {
        this(loadPixelMap(resource));
    }

    private static PixelMap createDummyPixelMap() {
        PixelMap.InitializationOptions options = new PixelMap.InitializationOptions();
        options.size = new Size(1, 1);
        options.pixelFormat = PixelFormat.ARGB_8888;
        return PixelMap.create(options);
    }

    private static PixelMap loadPixelMap(Resource resource) {
        ImageSource imageSource = ImageSource.create(resource, new ImageSource.SourceOptions());
        return imageSource.createPixelmap(new ImageSource.DecodingOptions());
    }

    private int pixelmapWidth = 0;
    private int pixelmapHeight = 0;

    private int paddingLeft = 0;
    private int paddingRight = 0;
    private int paddingTop = 0;
    private int paddingBottom = 0;

    List<Pair<Integer, Integer>> xPatches = new ArrayList<>();
    List<Pair<Integer, Integer>> yPatches = new ArrayList<>();

    private void init() {
        pixelmapWidth = mUserPixelMap.getImageInfo().size.width;
        pixelmapHeight = mUserPixelMap.getImageInfo().size.height;
        initPatches();
        initPaddings();
    }

    public int getIntrinsicWidth() {
        return pixelmapWidth - 2;
    }

    public int getIntrinsicHeight() {
        return pixelmapHeight - 2;
    }

    private void initPatches() {
        int patchLeft = 0;
        int patchTop = 0;
        int patchRight = 0;
        int patchBottom = 0;

        boolean findPatch;
        Position pos;

        // find horizontal
        for (findPatch = false, pos = new Position(0, 0); pos.posX < pixelmapWidth; pos.posX++) {
            if (((mUserPixelMap.readPixel(pos) >> 24) & 0xFF) != 0x00) { // not fully transparent
                if (!findPatch) {
                    patchLeft = pos.posX;
                    findPatch = true;
                }
            } else {
                if (findPatch) {
                    patchRight = pos.posX;
                    xPatches.add(new Pair<>(patchLeft, patchRight));
                    findPatch = false;
                }
            }
        }

        // find vertical
        for (findPatch = false, pos = new Position(0, 0); pos.posY < pixelmapHeight; pos.posY++) {
            if (((mUserPixelMap.readPixel(pos) >> 24) & 0xFF) != 0x00) { // not fully transparent
                if (!findPatch) {
                    patchTop = pos.posY;
                    findPatch = true;
                }
            } else {
                if (findPatch) {
                    patchBottom = pos.posY;
                    yPatches.add(new Pair<>(patchTop, patchBottom));
                    findPatch = false;
                }
            }
        }
    }

    public Paint getPaint() {
        if (mPaint == null) {
            mPaint = new Paint();
            mPaint.setDither(true);
        }
        return mPaint;
    }

    private void initPaddings() {
        boolean findPatch;
        Position pos;

        // find horizontal
        for (findPatch = false, pos = new Position(0, pixelmapHeight - 1); pos.posX < pixelmapWidth; pos.posX++) {
            if (((mUserPixelMap.readPixel(pos) >> 24) & 0xFF) != 0x00) { // not fully transparent
                if (!findPatch) {
                    paddingLeft = pos.posX;
                    findPatch = true;
                }
            } else {
                if (findPatch) {
                    paddingRight = pos.posX;
                    break;
                }
            }
        }

        // find vertical
        for (findPatch = false, pos = new Position(pixelmapWidth - 1, 0); pos.posY < pixelmapHeight; pos.posY++) {
            if (((mUserPixelMap.readPixel(pos) >> 24) & 0xFF) != 0x00) { // not fully transparent
                if (!findPatch) {
                    paddingTop = pos.posY;
                    findPatch = true;
                }
            } else {
                if (findPatch) {
                    paddingBottom = pos.posY;
                    break;
                }
            }
        }
    }

    public boolean getPadding(Rect rect) {
        rect.set(paddingLeft - 1, paddingTop - 1, pixelmapWidth - paddingRight, pixelmapHeight - paddingBottom);
        return true;
    }


    @Override
    public void setColorMatrix(ColorMatrix matrix) {
        super.setColorMatrix(matrix);
        if (mPaint == null && matrix == null) {
            // Fast common case -- leave at no color filter.
            return;
        }
        getPaint().setColorMatrix(matrix);
    }

    @Override
    public void setBounds(Rect bounds) {
        super.setBounds(bounds);
    }

    @Override
    public void drawToCanvas(Canvas canvas) {
        Rect bounds = getBounds();
        int viewWidth = bounds.getWidth();
        int viewHeight = bounds.getHeight();

        int xFlexibleSize = 0;
        int yFlexibleSize = 0;
        for (Pair<Integer, Integer> pair : xPatches) {
            xFlexibleSize += pair.s - pair.f;
        }
        for (Pair<Integer, Integer> pair : yPatches) {
            yFlexibleSize += pair.s - pair.f;
        }
        int xFixedSize = pixelmapWidth - 2 - xFlexibleSize;
        int yFixedSize = pixelmapHeight - 2 - yFlexibleSize;
        float xScale = (viewWidth - xFixedSize) / (float) xFlexibleSize;
        float yScale = (viewHeight - yFixedSize) / (float) yFlexibleSize;

        PixelMapHolder holder = new PixelMapHolder(mUserPixelMap);

        int xPartsSize = 2 * xPatches.size() + 2;
        int yPartsSize = 2 * yPatches.size() + 2;
        int[] xParts = new int[xPartsSize];
        int[] yParts = new int[yPartsSize];
        int[] XParts = new int[xPartsSize];
        int[] YParts = new int[yPartsSize];
        xParts[0] = yParts[0] = 1;
        XParts[0] = YParts[0] = 0;
        xParts[xPartsSize - 1] = pixelmapWidth - 1;
        yParts[yPartsSize - 1] = pixelmapHeight - 1;
        XParts[xPartsSize - 1] = viewWidth;
        YParts[yPartsSize - 1] = viewHeight;
        int i, j;
        for (i = 0; i < xPatches.size(); i++) {
            xParts[i * 2 + 1] = xPatches.get(i).f;
            xParts[i * 2 + 2] = xPatches.get(i).s;
        }
        for (i = 0; i < yPatches.size(); i++) {
            yParts[i * 2 + 1] = yPatches.get(i).f;
            yParts[i * 2 + 2] = yPatches.get(i).s;
        }

        for (i = 1; i < xParts.length - 1; i += 2) {
            XParts[i] = XParts[i - 1] + (xParts[i] - xParts[i - 1]);
            XParts[i + 1] = XParts[i] + (int) Math.floor((xParts[i + 1] - xParts[i]) * xScale);
        }
        for (i = 1; i < yParts.length - 1; i += 2) {
            YParts[i] = YParts[i - 1] + (yParts[i] - yParts[i - 1]);
            YParts[i + 1] = YParts[i] + (int) Math.floor((yParts[i + 1] - yParts[i]) * yScale);
        }

        RectFloat src = new RectFloat();
        RectFloat dst = new RectFloat();
        int x0, y0, x1, y1, X0, Y0, X1, Y1;
        for (j = 0; j < yParts.length - 1; j++) {
            y0 = yParts[j];
            y1 = yParts[j + 1];
            Y0 = YParts[j];
            Y1 = YParts[j + 1];
            for (i = 0; i < xParts.length - 1; i++) {
                x0 = xParts[i];
                x1 = xParts[i + 1];
                X0 = XParts[i];
                X1 = XParts[i + 1];
                src.modify(x0, y0, x1, y1);
                dst.modify(X0, Y0, X1, Y1);
                dst.translate(bounds.left, bounds.top);
                canvas.drawPixelMapHolderRect(holder, src, dst, getPaint());
            }
        }
    }
}
